package edu.asu.sapa.ground.update;

import edu.asu.sapa.Planner;
import edu.asu.sapa.ground.MathForm;
import edu.asu.sapa.ground.Operator;
import edu.asu.sapa.ground.Problem;
import edu.asu.sapa.ground.PropDB;
import edu.asu.sapa.ground.Proposition;
import edu.asu.sapa.ground.State;
import edu.asu.sapa.ground.PropDB.Entry;
import edu.asu.sapa.heuristic.PG;

public class SoftCondition extends Condition {

	public float reward;
	public float penalty;

	public SoftCondition(Proposition p, int id, boolean value, float reward, float penalty) {
		super(p, id, value);
		this.reward = reward;
		this.penalty = penalty;
	}
	
	public void relaxedUpdate(PG pg, State s, Context c, float time) {
		if (value)
			pg.addGoal(id,time,reward,penalty);
	}
	public void relaxedStart(PG pg, State s, Context c, float time) {
		if (value)
			pg.addGoal(id,time,reward,penalty);
		// not really appropriate to attempt to fake negative conditions by inverting reward/penalty
		// should just propagate negative literal reachability separately.
	}
	public void relaxedFinish(PG pg, State s, Context c, float time) {
	}

	@Override
	public boolean updateBefore(State s, Context c, float time) {
		if (isConstant && value) {
			s.gCost -= reward;
		} else {
			PropDB db = s.propDB;
			if (db.readValueBefore(id,time,value)) {
				s.gCost -= reward;
			} else {
				s.gCost += penalty;
			}
		}
		return s.gCost < Float.POSITIVE_INFINITY;
	}
	@Override
	public boolean updateAfter(State s, Context c, float time) {
		if (isConstant && value) {
			s.gCost -= reward;
		} else {
			PropDB db = s.propDB;
			if (db.readValueAt(id,time,value)) {
				s.gCost -= reward;
			} else {
				s.gCost += penalty;
			}
		}
		return s.gCost < Float.POSITIVE_INFINITY;
	}

	@Override
	public boolean holdsAt(State s, Context c) {
		PropDB db = s.propDB;
		float time = s.time;
		if (!db.readValueAt(id,time,value)) {
			s.removeConstraint(this,c);
			return (s.gCost += penalty) < Float.POSITIVE_INFINITY;
		}
		return true;
	}
	@Override
	public boolean holdsBefore(State s, Context c) {
		PropDB db = s.propDB;
		float time = s.time;
		if (!db.readValueBefore(id,time,value)) {
			s.removeConstraint(this,c);
			return (s.gCost += penalty) < Float.POSITIVE_INFINITY;
		}
		return true;
	}
	@Override
	public boolean finishBefore(State s, Context c, float time) {
		if (s.removeConstraint(this,c))
			s.gCost -= reward;
		return true;
	}
	@Override
	public boolean finishAfter(State s, Context c, float time) {
		if (s.removeConstraint(this,c))
			s.gCost -= reward;
		return true;
	}
	@Override
	public boolean startBefore(State s, Context c, float time) {
		PropDB db = s.propDB;
		if (db.readValueBefore(id,time,value)) {
			s.addConstraint(this,c);
		} else {
			s.gCost += penalty;
		}
		return s.gCost < Float.POSITIVE_INFINITY;
	}
	@Override
	public boolean startAfter(State s, Context c, float time) {
		PropDB db = s.propDB;
		if (db.readValueAt(id,s.time,value)) {
			s.addConstraint(this,c);
		} else {
			s.gCost += penalty;
		}
		return s.gCost < Float.POSITIVE_INFINITY;
	}
	
	public boolean applicableBefore(State s, Context c) {
		PropDB db = s.propDB;
		float time = s.time;
		return s.gCost + (db.getValueBefore(id,time,value) ? -reward : penalty) < Float.POSITIVE_INFINITY;
	}
	
	public boolean applicableAfter(State s, Context c) {
		PropDB db = s.propDB;
		float time = s.time;
		return s.gCost + (db.getValueAt(id,time,value) ? -reward : penalty) < Float.POSITIVE_INFINITY;
	}
	
	public int compareTo(SoftCondition o) {
		// assert this != null && o != null;
//		if (isConstant || o.isConstant) {
//			if (isConstant && o.isConstant) {
//				if (!value && o.value)
//					return -1;
//				if (value && !o.value)
//					return 1;
//				return 0;
//			}
//			if (isConstant) {
//				if (!value)
//					return -1;
//				return 1;
//			}
//			if (!o.value)
//				return 1;
//			return -1;
//		}
		if (id < o.id)
			return -1;
		if (id > o.id)
			return 1;
		if (value != o.value)
			if (!value)
				return -1;
			else
				return 1;
		return (int) -((reward + penalty) - (o.reward + o.penalty));
	}

	public String toString() {
		return "(:goal (== " + id + " " + value + ") :reward " + reward + " :penalty " + penalty + ")";
	}

	// conditions return whether or not they are conceivably true, 
	// which is a) both dynamic conditions, and b) some static conditions
	public boolean analyzeStatic(float dur) {
		isConstant = false;
		if (p.isConstant) {
			isConstant = true;
			value = (value == p.value);
			id = 0;
			p = Problem.propositions.get(0);
			return value ? reward > Float.NEGATIVE_INFINITY : penalty < Float.POSITIVE_INFINITY;
		} else if (reward == Float.NEGATIVE_INFINITY && penalty == Float.POSITIVE_INFINITY) {
			isConstant = true;
			value = false;
			id = 0;
			p = Problem.propositions.get(0);
			return false;
		} else if (reward == 0 && penalty == 0) {
			makeTop();
			return true;
		}
		return true;
	}
	
	public boolean isNoOp() {
		if (isConstant) {
			if (value == true)
				return reward == 0;
			else
				return penalty == 0;
		}
		return false;
	}
	
	public boolean isFail() {
		if (isConstant) {
			if (value == true)
				return reward == Float.NEGATIVE_INFINITY;
			else
				return penalty == Float.POSITIVE_INFINITY;
		}
		return false;
	}
	
	@Override
	public void removeDependencies(Operator o) {
		if (penalty >= Float.POSITIVE_INFINITY) {
			super.removeDependencies(o);
		} else {
			// can't treat as hard condition...
		}
	}
	
	@Override
	public void setDependencies(Operator o) {
		if (penalty >= Float.POSITIVE_INFINITY) {
			super.setDependencies(o);
		} else {
			// can't treat as hard condition...
		}
	}
}
